var gl;
function initGL(canvas) {
    try {
        gl = canvas.getContext("experimental-webgl");
        gl.viewportWidth = canvas.width;
        gl.viewportHeight = canvas.height;
    } catch (e) {
    }
    if (!gl) {
        alert("Could not initialise WebGL, sorry :-(");
    }
}

//Gotten from...
function getShader(gl, id) {
    var shaderScript = document.getElementById(id);
    if (!shaderScript) {
        return null;
    }

    var str = "";
    var k = shaderScript.firstChild;
    while (k) {
        if (k.nodeType == 3) {
            str += k.textContent;
        }
        k = k.nextSibling;
    }

    var shader;
    if (shaderScript.type == "x-shader/x-fragment") {
        shader = gl.createShader(gl.FRAGMENT_SHADER);
    } else if (shaderScript.type == "x-shader/x-vertex") {
        shader = gl.createShader(gl.VERTEX_SHADER);
    } else {
        return null;
    }

    gl.shaderSource(shader, str);
    gl.compileShader(shader);

    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        alert(gl.getShaderInfoLog(shader));
        return null;
    }

    return shader;
}

var shaderProgram;
var waterShaderProgram;
var textureShaderProgram;
var objectShaderProgram;


function initShaders(fragmentShader, vertexShader) {
	var tempShader;
    tempShader = gl.createProgram();
    gl.attachShader(tempShader, vertexShader);
    gl.attachShader(tempShader, fragmentShader);
    gl.linkProgram(tempShader);

    if (!gl.getProgramParameter(tempShader, gl.LINK_STATUS)) {
        alert("Could not initialise shaders");
    }

    gl.useProgram(tempShader);
	// texture stuff
	
	textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord");  
    gl.enableVertexAttribArray(textureCoordAttribute);
	
	// end texture stuff
	var loc = gl.getAttribLocation(tempShader, "aVertexPosition");
	tempShader.vertexPositionAttribute = loc;
	if (loc !== -1) {
		gl.enableVertexAttribArray(loc);
	}

	loc = gl.getAttribLocation(tempShader, "aVertexNormal");
	tempShader.vertexNormalAttribute = loc;
	if (loc !== -1) {
		gl.enableVertexAttribArray(loc);
	}

	loc = gl.getAttribLocation(tempShader, "aVertexExtra");
	tempShader.vertexExtraAttribute = loc;
	if (loc !== -1) {
		gl.enableVertexAttribArray(loc);
	}

    tempShader.pMatrixUniform = gl.getUniformLocation(tempShader, "uPMatrix");
    tempShader.mvMatrixUniform = gl.getUniformLocation(tempShader, "uMVMatrix");
	tempShader.extraUniform = gl.getUniformLocation(tempShader, "extraUniform");
	tempShader.aColor = gl.getUniformLocation(tempShader, "aColor");
	tempShader.lightUniform = gl.getUniformLocation(tempShader, "lightUniform");
	
	tempShader.uCollisions = gl.getUniformLocation(tempShader, "uCollisions");
	tempShader.uTime = gl.getUniformLocation(tempShader, "uTime");
	tempShader.uCollisionSide = gl.getUniformLocation(tempShader, "uCollisionSide");
	tempShader.uExtraData = gl.getUniformLocation(tempShader, "uExtraData");

	return tempShader;
}

var rect;
var ball1;
var ramp;

function webGLStart() {
    var canvas = document.getElementById("canvas");
    initGL(canvas);
    shaderProgram = initShaders(getShader(gl, "normalshader-fs"), getShader(gl, "normalshader-vs"));
	waterShaderProgram = initShaders(getShader(gl, "watershader-fs"), getShader(gl, "watershader-vs"));
	textureShaderProgram = initShaders(getShader(gl, "textureshader-fs"), getShader(gl, "textureshader-vs"));
	objectShaderProgram = initShaders(getShader(gl, "objectshader-fs"), getShader(gl, "objectshader-vs"));
    rect = new RectModel(10.0, 10.0);
    rect.create();
}

var camera = mat4.create();

function masterDraw(){
	
	// texture stuff
	
	initTextures();
	gl.activeTexture(gl.TEXTURE0);  
	gl.bindTexture(gl.TEXTURE_2D, rectTexture);  
	gl.uniform1i(gl.getUniformLocation(shaderProgram, "uSampler"), 0);
	
	// end texture stuff
	 
    //console.log(tick);
    gl.clearColor(0.8, 0.8, 0.8, 1.0);
    gl.enable(gl.DEPTH_TEST);

    //Camera
    mat4.ortho(0, gl.viewportWidth, -gl.viewportHeight, 0, -1000, 1000, camera);
	mat4.translate(camera, vec3.create([0, -100, 400]));
	mat4.rotateX(camera, 0.5, camera);
	mat4.rotateY(camera, 0.25, camera);

    //mat4.perspective(90, gl.viewportWidth / gl.viewportHeight, 0.1, 10000.0, camera);
    //mat4.translate(camera, vec3.create([-gl.viewportWidth / 2.0, gl.viewportHeight / 2.0, -320.0]), camera);

    gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
}

function bindBuffer(buffer, attribute) {
	if (attribute === -1) return;
	gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
	gl.vertexAttribPointer(attribute, buffer.itemSize, gl.FLOAT, false, 0, 0);
}

function prepareDraw(camera) {
	mat4.identity(this.mvMatrix);
	mat4.translate(this.mvMatrix, this.pos);
    mat4.rotateZ(this.mvMatrix, this.angle, this.mvMatrix);

	gl.useProgram(this.shader);

	bindBuffer(this.vertexBuffer, this.shader.vertexPositionAttribute);
	bindBuffer(this.normalBuffer, this.shader.vertexNormalAttribute);
	bindBuffer(this.extraVertexBuffer, this.shader.vertexExtraAttribute);

	this.setMatrixUniforms(camera);
}

function setUniformM4fv(loc, mat) {
    if (loc) {
		gl.uniformMatrix4fv(loc, false, mat);
    }
}

function setUniform4fv(loc, vec) {
    if (loc) {
		gl.uniform4fv(loc, vec);
    }
}

function setUniform3fv(loc, vec) {
	if(loc){
		gl.uniform3fv(loc, vec);
	}
}

function setUniform1f(loc, val) {
    if (loc) {
		gl.uniform1f(loc, val);
    }
}

function setMatrixUniforms(camera) {
    setUniformM4fv(this.shader.pMatrixUniform, camera);
    setUniformM4fv(this.shader.mvMatrixUniform, this.mvMatrix);

    setUniform4fv(this.shader.aColor, this.color);

    setUniformM4fv(this.shader.uCollisions, this.uCollisions);
	setUniform3fv(this.shader.lightUniform, [0.0, 500.0, 300.0]);
    setUniform4fv(this.shader.extraUniform, this.extraUniform);
    setUniform4fv(this.shader.uCollisionSide, this.uCollisionSide);
    setUniform4fv(this.shader.uExtraData, this.uExtraData);

    setUniform1f(this.shader.uTime, this.uTime);
}

function initBuffer(arr, itemSz) {
    var ret = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, ret);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(arr), gl.STATIC_DRAW);
    ret.itemSize = itemSz;
    ret.numItems = arr.length / itemSz;
    return ret;
}

function initBuffers() {
    gl.useProgram(this.shader);

    this.vertexBuffer = initBuffer(this.getVertexData(), 3);
    this.normalBuffer = initBuffer(this.getNormalData(), 3);

	if (this.getExtraVertexData().length == this.getNormalData().length) {
        this.extraVertexBuffer = initBuffer(this.getExtraVertexData(), 3);
	}
}

function calcSide(normal) {
    return 0;
}

function calcForce(decomp) {
    return length(decomp[1]);
}

function calcCollision(side, force, pos, time) {
    return [pos[0], -pos[1], time, force];
}

function pushCollision(side, force, pos, time) {
    this.uCollisionSide = [
        side,
        this.uCollisionSide[0],
        this.uCollisionSide[1],
        this.uCollisionSide[2],
    ];

    for (var i = 3; i > 0; i--) {
        for (var j = 0; j < 4; j++) {
            this.uCollisions[i * 4 + j] = this.uCollisions[(i - 1) * 4 + j];
        }
    }

    var collision = calcCollision(side, force, pos, time);

    for (var j = 0; j < 4; j++) {
        this.uCollisions[j] = collision[j];
    }
}

function ignoreCollision(decomp) {
    return length(decomp[1]) < 1.0;
}

function setDefaults(obj) {
	obj.shader = shaderProgram;
    obj.angle = 0;
	obj.vertexData = [];
	obj.normalData = [];
	obj.extraVertexData = [];
    obj.update = function() { this.tm += 10; };
    obj.tm = 0;
	obj.color = [0.8, 0.8, 0.8, 1.0];
	obj.extraUniform = [0, 0, 0, 0];
	obj.initBuffers = initBuffers;
	obj.setMatrixUniforms = setMatrixUniforms;
	obj.prepareDraw = prepareDraw;
	obj.getVertexData = function(){
		return this.vertexData;	
	}
	
	obj.getNormalData = function(){
		return this.normalData;
	}
	obj.getExtraVertexData = function(){
		return this.extraVertexData;
	}
	obj.mvMatrix = mat4.create();

    obj.uCollisions = mat4.create();

    obj.uCollisionSide = [0, 0, 0, 0];
    obj.uExtraData = [0, 0, 0, 0];

    obj.uTime = 0;

    obj.update = function(obj) { this.uTime += 10; };

    obj.pushCollision = pushCollision;
	obj.ignoreCollision = ignoreCollision;
    obj.collide = function(normal, decomp, pos) {
        if (!this.ignoreCollision(decomp)) {
            this.pushCollision(calcSide(normal), calcForce(decomp), pos, this.uTime);
        }
    };
}


// texture stuff

    function initTextures() {  
      rectTexture = gl.createTexture();  
      //rectImage = new Image();  
      //rectImage.onload = function() { handleTextureLoaded(rectImage, rectTexture); }  
      //rectImage.src = "randomtexture.jpg";  
    }  
      
    function handleTextureLoaded(image, texture) {  
      gl.bindTexture(gl.TEXTURE_2D, texture);  
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);  
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);  
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);  
      gl.generateMipmap(gl.TEXTURE_2D);  
      gl.bindTexture(gl.TEXTURE_2D, null);  
    }  

// end texture stuff

/* 
 * Texture loading, including shaders, and texture loading methods came from
 * https://developer.mozilla.org/en/WebGL/Using_textures_in_WebGL
 * "Using textures in WebGL
 */
